
#include "Geometry.h"

#include <vector>
#include "D3D11.h"

typedef std::vector<Vertex> VertexVec;
typedef std::vector<DWORD> IndexVec;

inline XMFLOAT3 rotateFloat3(XMFLOAT3 vector, XMMATRIX * rotMat)
{
	XMFLOAT3 res;
	XMVECTOR xmVector = XMLoadFloat3(&vector);
	xmVector = XMVector3Transform(xmVector, *rotMat);
	XMStoreFloat3(&res, xmVector);
	return res;
}

void Geometry::CreateTiledQuad(DWORD numRows, DWORD numCols, float dx, MeshData** outMeshData, bool stretchTexture)
{
	DWORD m = numRows;
	DWORD n = numCols;

	DWORD numVertices = m*n;
	DWORD numFaces    = (m-1)*(n-1)*2;

	// Create the geometry and fill the vertex buffer. 
	
	*outMeshData = new MeshData();
	
	MeshData* &meshData = *outMeshData;

	meshData->mpVertices = new Vertex[numVertices];
	meshData->mVertexCount = numVertices;

	Vertex* pVertices = meshData->mpVertices;

	float halfWidth = (n-1)*dx*0.5f;
	float halfDepth = (m-1)*dx*0.5f;

	for(DWORD i = 0; i < m; ++i)
	{
		float z = halfDepth - i*dx;
		for(DWORD j = 0; j < n; ++j)
		{
			float x = -halfWidth + j*dx;

			pVertices[i*n+j].mPosition	= XMFLOAT3(x, 0.0f, z);
			pVertices[i*n+j].mNormal	= XMFLOAT3(0.0f, 1.0f, 0.0f);
			pVertices[i*n+j].mTangent	= XMFLOAT3(1.0f, 0.0f, 0.0f);			
		}
	}

	if (stretchTexture == true)
	{
		float du = 1.0f / (n-1);
		float dv = 1.0f / (m-1);
		for(DWORD i = 0; i < m; ++i)
		{
			for(DWORD j = 0; j < n; ++j)
			{
				// Stretch texture over grid.
				pVertices[i*n+j].mTexC.x = j*du;
				pVertices[i*n+j].mTexC.y = i*dv;
			}
		}
	}
	else
	{
		for(DWORD i = 0; i < m; ++i)
		{
			for(DWORD j = 0; j < n; ++j)
			{
				pVertices[i*n+j].mTexC.x = j;
				pVertices[i*n+j].mTexC.y = i;
			}
		}
	}
	

	int mSubsetCount;
	SubsetData* mpSubsets;

	// Create the index buffer. 

	meshData->mIndexCount = numFaces*3;
	meshData->mpIndices = new DWORD[numFaces*3];

	DWORD* pIndices = meshData->mpIndices;

	// Iterate over each quad and compute indices.
	int k = 0;
	for(DWORD i = 0; i < m-1; ++i)
	{
		for(DWORD j = 0; j < n-1; ++j)
		{
			pIndices[k]   = i*n+j;
			pIndices[k+1] = i*n+j+1;
			pIndices[k+2] = (i+1)*n+j;

			pIndices[k+3] = (i+1)*n+j;
			pIndices[k+4] = i*n+j+1;
			pIndices[k+5] = (i+1)*n+j+1;

			k += 6; // next quad
		}
	}

	meshData->mSubsetCount = 1;
	meshData->mpSubsets = new SubsetData;

	SubsetData* subSet = meshData->mpSubsets;

	subSet->mStartIndex = 0;
	subSet->mTriangleCount = numFaces;
	
	subSet->mWorld;
	XMStoreFloat4x4(&subSet->mWorld, XMMatrixIdentity());

	subSet->mDiffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	subSet->mSpecular = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
	subSet->mAmbient = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	subSet->mEmission = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
	subSet->mShininess = 0;

	subSet->mDiffuseMapFilePath = std::string();
	subSet->mNormalMapFilePath = std::string();

}

void Geometry::CreateScreenQuad(XMFLOAT2 topLeft, XMFLOAT2 bottomRight, MeshData** outMeshData)
{
	FLOAT top = - ( 2 * topLeft.y - 1);
	FLOAT left =  2 * topLeft.x - 1;
	FLOAT bottom = - ( 2 * bottomRight.y - 1);
	FLOAT right = 2 * bottomRight.x - 1;

	XMFLOAT3 pos[] = 
	{
		XMFLOAT3(left, bottom, 0.0f),
		XMFLOAT3(left,  top, 0.0f),
		XMFLOAT3(right,  top, 0.0f),

		XMFLOAT3(left, bottom, 0.0f),
		XMFLOAT3(right, top, 0.0f),
		XMFLOAT3(right, bottom, 0.0f)
	};

	XMFLOAT2 tex[] = 
	{
		XMFLOAT2(0.0f, 1.0f),
		XMFLOAT2(0.0f, 0.0f),
		XMFLOAT2(1.0f, 0.0f),

		XMFLOAT2(0.0f, 1.0f),
		XMFLOAT2(1.0f, 0.0f),
		XMFLOAT2(1.0f, 1.0f)
	};

	*outMeshData = new MeshData();
	
	MeshData* &meshData = *outMeshData;

	meshData->mpVertices = new Vertex[6];
	meshData->mVertexCount = 6;

	for(int i = 0; i < 6; ++i)
	{
		meshData->mpVertices[i].mPosition = pos[i];
		meshData->mpVertices[i].mTexC = tex[i];
	}

	meshData->mIndexCount = 2*3;
	meshData->mpIndices = new DWORD[2*3];

	DWORD* pIndices = meshData->mpIndices;

	for(DWORD i = 0; i < 6; ++i)
	{
		meshData->mpIndices[i] = i;
	}

	meshData->mSubsetCount = 1;
	meshData->mpSubsets = new SubsetData;

	SubsetData* subSet = meshData->mpSubsets;

	subSet->mStartIndex = 0;
	subSet->mTriangleCount = 2;
	
	XMStoreFloat4x4(&subSet->mWorld, XMMatrixIdentity());

	subSet->mDiffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	subSet->mSpecular = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
	subSet->mAmbient = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	subSet->mEmission = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
	subSet->mShininess = 0;

	subSet->mDiffuseMapFilePath = std::string();
	subSet->mNormalMapFilePath = std::string();
}

/************************
*
* Axis
*
*************************/


void CreateXAxis(float length, float width, VertexVec &vertices, IndexVec &indices)
{
	float halfW = width * 0.5;
	
	DWORD numVertices = 16;
	DWORD numFaces    = numVertices / 2;

	vertices.resize(numVertices);
	
	XMFLOAT3 normal(0,0,-1);
	XMFLOAT3 tangent(1,0,0);

		// First quad

	vertices[0].mPosition = XMFLOAT3(0, halfW, -halfW);
	vertices[0].mNormal = normal;
	vertices[0].mTangent = tangent;
	vertices[0].mTexC = XMFLOAT2(0, 0);
	
	vertices[0+1].mPosition = XMFLOAT3(length, halfW, -halfW);
	vertices[0+1].mNormal = normal;
	vertices[0+1].mTangent = tangent;
	vertices[0+1].mTexC = XMFLOAT2(0, 1);

	vertices[0+2].mPosition = XMFLOAT3(length, -halfW, -halfW);
	vertices[0+2].mNormal = normal;
	vertices[0+2].mTangent = tangent;
	vertices[0+2].mTexC = XMFLOAT2(1, 1);

	vertices[0+3].mPosition = XMFLOAT3(0, -halfW, -halfW);
	vertices[0+3].mNormal = normal;
	vertices[0+3].mTangent = tangent;
	vertices[0+3].mTexC = XMFLOAT2(0, 1);

	XMMATRIX rotMat;

	float halfPi = 3.14159265 / 2;
	
	XMVECTOR vector;

		// Other three quads

	for (int i = 1; i < 4; i++)
	{
		rotMat = XMMatrixRotationX(halfPi * i);

		for (int j = 0; j < 4; j++)
		{
			vertices[4 * i + j].mPosition = rotateFloat3(vertices[j].mPosition, &rotMat);
			vertices[4 * i + j].mNormal = rotateFloat3(vertices[j].mNormal, &rotMat);

			vertices[4 * i + j].mTangent = vertices[j].mTangent;
			vertices[4 * i + j].mTexC = vertices[j].mTexC;
		}
	}

	indices.resize(3 * 2 * 4);

	int offset = 0;

	for (int i = 0; i < 4; i++)
	{
		indices[(i*6) + 0] = 0 + offset; 
		indices[(i*6) + 1] = 1 + offset; 
		indices[(i*6) + 2] = 2 + offset;

		indices[(i*6) + 3] = 2 + offset; 
		indices[(i*6) + 4] = 3 + offset; 
		indices[(i*6) + 5] = 0 + offset;

		offset += 4;
	}
}

void CopyAndRotateAxis(VertexVec &inVertices, IndexVec &inIndices, XMMATRIX * rotMat, VertexVec &outVertices, IndexVec &outIndices)
{
	outVertices.resize(inVertices.size());

	for (int i = 0; i < inVertices.size(); i++)
	{
		outVertices[i].mPosition = rotateFloat3(inVertices[i].mPosition, rotMat);
		outVertices[i].mNormal = rotateFloat3(inVertices[i].mNormal, rotMat);
		outVertices[i].mTangent = rotateFloat3(inVertices[i].mTangent, rotMat);
		outVertices[i].mTexC = inVertices[i].mTexC;
	}
	
	outIndices.resize(inIndices.size());

	for (int i = 0; i < inIndices.size(); i++)
	{
		outIndices[i] = inIndices[i];
	}
}

// Public

void Geometry::CreateAxes(FLOAT length, FLOAT width, MeshData** outMeshData)
{
	FLOAT halfPi = 3.14159265 / 2;

	int numIndicesPerAxis;

	*outMeshData = new MeshData();
	
	MeshData* &meshData = *outMeshData;

	VertexVec verticesX, verticesY, verticesZ, vertices;
	IndexVec indicesX, indicesY, indicesZ, indices;

	CreateXAxis(length, width, verticesX, indicesX);	

	numIndicesPerAxis = indicesX.size();
	
	XMMATRIX rotMat = XMMatrixRotationZ(halfPi);

	CopyAndRotateAxis(verticesX, indicesX, &rotMat, verticesY, indicesY);

	rotMat = XMMatrixRotationY(-halfPi);
	
	CopyAndRotateAxis(verticesX, indicesX, &rotMat, verticesZ, indicesZ);
	
		// Copy from axis vectors to output vectors

	VertexVec * vertVecs[] = {&verticesX, &verticesY, &verticesZ};
	IndexVec * indVecs[] = {&indicesX, &indicesY, &indicesZ};

	for (int i = 0; i < 3; i++)
	{
		int offset = vertices.size();

		for (int j = 0; j < vertVecs[i]->size(); j++)
		{
			vertices.push_back(vertVecs[i]->at(j));
		}
		
		for (int j = 0; j < indVecs[i]->size(); j++)
		{
			indices.push_back(indVecs[i]->at(j) + offset);
		}


	}

	// Copy to mesh data

	meshData->mVertexCount = vertices.size();
	meshData->mpVertices = new Vertex[meshData->mVertexCount];

	for (int i = 0; i < vertices.size(); i++)
	{
		meshData->mpVertices[i] = vertices[i];
	}

	meshData->mIndexCount = indices.size();
	meshData->mpIndices = new DWORD[meshData->mIndexCount];

	for (int i = 0; i < indices.size(); i++)
	{
		meshData->mpIndices[i] = indices[i];
	}

	meshData->mSubsetCount = 3;

	meshData->mpSubsets = new SubsetData[3];

	for (int i = 0; i < 3; i++)
	{
		SubsetData* subset = &meshData->mpSubsets[i];

		XMStoreFloat4x4(&subset->mWorld, XMMatrixIdentity());
	
		XMStoreFloat4(&subset->mAmbient, XMVectorSplatOne());
		XMStoreFloat4(&subset->mDiffuse, XMVectorSplatOne());
		XMStoreFloat4(&subset->mEmission, XMVectorSplatOne());
		subset->mShininess = 0;
		XMStoreFloat4(&subset->mSpecular, XMVectorZero());

		subset->mStartIndex = i * numIndicesPerAxis;
		subset->mTriangleCount = numIndicesPerAxis / 3;
	}
}